package com.anivia.common.log;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.anivia.common.core.constant.CommonStant;
import com.anivia.common.log.util.HttpWrapperUtil;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;

import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 日志处理
 *
 * @author wangj
 */
@Slf4j
public class DefaultRequestLogHandler implements RequestLogHandler {
    /**
     * 请求开始时间的key。
     */
    private static final String START_KEY = "start";

    /**
     * 请求持续时间的key。
     */
    private static final String CONS_KEY = "cons";

    /**
     * 请求URL的key。
     */
    private static final String URL_KEY = "url";

    /**
     * 请求IP地址的key。
     */
    private static final String IP_KEY = "ip";

    /**
     * 登录用户类型的key。
     */
    private static final String LOGIN_TYPE = CommonStant.LOGIN_TYPE;

    /**
     * 登录用户ID的key。
     */
    private static final String LOGIN_ID = CommonStant.LOGIN_ID;

    /**
     * 排除的日志URL映射。
     */
    private ConcurrentHashMap<String, String> ignoreParamUrls;


    @Override
    public void printLog(HttpServletRequest request, AniviaResponseWrapper response) {
        try {
            // 获取日志起始时间
            String start = MDC.get(START_KEY);
            if (StrUtil.isBlank(start)) {
                log.error("日志记录未初始化！");
            } else {
                // 将起始时间转换为长整型，并更新MDC
                long startMs = Long.parseLong(start);
                MDC.put(START_KEY, DateUtil.format(new Date(startMs), DatePattern.ISO8601_FORMATTER));
                MDC.put(CONS_KEY, (System.currentTimeMillis() - startMs) + "");
            }
            // 更新MDC中的URL和登录类型
            MDC.put(URL_KEY, request.getRequestURI());
            MDC.put(LOGIN_TYPE, request.getHeader(LOGIN_TYPE));

            // 创建日志对象
            JSONObject logObj = new JSONObject();

            // 获取IP TODO
            // MDC.put(IP_KEY, IP);
            logObj.putOpt(URL_KEY, request.getRequestURI());
            logObj.putOpt(CONS_KEY, MDC.get(CONS_KEY) + "ms");
            logObj.putOpt(IP_KEY, MDC.get(IP_KEY));
            logObj.putOpt(LOGIN_TYPE, MDC.get(LOGIN_TYPE));
            logObj.putOpt(LOGIN_ID, MDC.get(LOGIN_ID));
//            logObj.putOpt("headers", getHeaders(request));

            // 判断是否需要排除日志记录
            if (HttpWrapperUtil.isExcludeUrl(request, ignoreParamUrls)) {
            } else {
                // 判断请求是否为AniviaRequestWrapper类型，如果是则将请求体直接添加到日志对象中，否则将请求参数添加到日志对象中
                if (request instanceof AniviaRequestWrapper) {
                    String body = new String(((AniviaRequestWrapper) request).getRequestBody(), StandardCharsets.UTF_8);
                    if (StrUtil.isNotBlank(body) && JSONUtil.isTypeJSON(body)) {
                        logObj.putOpt("requestPayload", JSONUtil.parseObj(body));
                    }
                    if (StrUtil.isNotBlank(body) && !JSONUtil.isTypeJSON(body)) {
                        logObj.putOpt("requestPayload", body);
                    }
                } else {
                    Map<String, String[]> parameterMap = request.getParameterMap();
                    if (MapUtil.isNotEmpty(parameterMap)) {
                        List<String> paramList = new ArrayList<>();
                        for (String key : parameterMap.keySet()) {
                            String[] values = parameterMap.get(key);
                            for (String value : values) {
                                paramList.add(key + "=" + value);
                            }
                        }
                        logObj.putOpt("requestPayload", CollUtil.join(paramList, "&"));
                    } else {
                        logObj.putOpt("requestPayload", null);
                    }
                }
            }

            // 判断是否需要记录响应体
            if (HttpWrapperUtil.isGetBody(response)) {
                String body = new String(response.getResponseBody(), StandardCharsets.UTF_8);
                if (StrUtil.isNotBlank(body) && JSONUtil.isTypeJSON(body)) {
                    logObj.putOpt("response", JSONUtil.parseObj(body));
                }
                if (StrUtil.isNotBlank(body) && !JSONUtil.isTypeJSON(body)) {
                    logObj.putOpt("response", body);
                }
            }
            // 输出请求日志
            log.info("请求日志：{}", JSONUtil.toJsonStr(logObj));
        } catch (Throwable e) {
            log.error("请求日志记录异常", e);
        } finally {
            // 清除MDC
            clear();
        }
    }


    @Override
    public void init(HttpServletRequest request) {
        MDC.put(START_KEY, System.currentTimeMillis() + "");
    }

    /**
     * 清除所有的MDC变量
     */
    @Override
    public void clear() {
        MDC.remove(START_KEY);
        MDC.remove(CONS_KEY);
        MDC.remove(URL_KEY);
        MDC.remove(IP_KEY);
        MDC.remove(LOGIN_TYPE);
    }

    /**
     * 获取请求头信息
     *
     * @param request HTTP请求对象
     * @return 包含请求头信息的Map对象
     */
    private Map<String, String> getHeaders(HttpServletRequest request) {
        // 需要打印的请求头
        Map<String, String> map = new HashMap<>();
        Enumeration<String> headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String key = headerNames.nextElement();
            String value = request.getHeader(key);
            map.put(key, value);
        }
        return map;
    }

}
